<!DOCTYPE html>
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="UTF-8">

  <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../../web-component-tester/browser.js"></script>

  <link rel="import" href="../../polymer.html">
  <link rel="import" href="events-elements.html">
</head>
<body>

  <script>
    suite('Event Listeners', function() {
      var el;

      suite('listeners block', function() {
        suiteSetup(function() {
          el = document.createElement('x-listeners');
          document.body.appendChild(el);
        });

        suiteTeardown(function() {
          document.body.removeChild(el);
        });

        test('setup', function() {
          var boundListeners = el.__boundListeners;
          assert(boundListeners, 'listeners are bound');
          var map = boundListeners.get(el);
          assert(map, 'events have handlers');
          Object.keys(el.listeners).forEach(function(l) {
            var key = Polymer.Base._boundListenerKey(l, el.listeners[l]);
            assert(map[key], 'listener is in event map');
          });
        });

        test('fire', function() {
          el.fire('foo');
          assert.equal(el._handled[el.localName], 'foo');
          el.fire('bar');
          assert.equal(el._warned.length, 1);
          assert.equal(el._warned[0], 'listener method `missing` not defined');
        });

        test('teardown', function() {
          el.teardown();
          assert.deepEqual(el._removed[0], {target: el.localName, event: 'foo'});
          assert.deepEqual(el._removed[1], {target: el.localName, event: 'bar'});
        });
      });

      suite('on-*', function() {

        suiteSetup(function() {
          el = document.createElement('x-on');
          document.body.appendChild(el);
        });

        suiteTeardown(function() {
          document.body.removeChild(el);
        });

        test('setup', function() {
          var boundListeners = el.__boundListeners;
          assert(boundListeners, 'listeners are bound');
          var map = boundListeners.get(el.$.inner);
          assert(map, 'events have handlers');
          assert(el._notes[0].events.length, 2);
          el._notes[0].events.forEach(function(l) {
            var key = Polymer.Base._boundListenerKey(l.name, l.value);
            assert(map[key], 'listener is in event map');
          });
        });

        test('fire', function() {
          var options = {node: el.$.inner};
          el.fire('foo', {}, options);
          assert.equal(el._handled.div, 'foo');
          assert(!el._handled[el.localName]);
          el.fire('bar', {}, options);
          assert.equal(el._warned.length, 1);
          assert.equal(el._warned[0], 'listener method `missing` not defined');
        });

        test('teardown', function() {
          el.teardown();
          assert.deepEqual(el._removed[0], {target: 'div', event: 'foo'});
          assert.deepEqual(el._removed[1], {target: 'div', event: 'bar'});
        });
      });

      suite('dynamic', function() {

        suiteSetup(function() {
          el = document.createElement('x-dynamic');
          document.body.appendChild(el);
        });

        suiteTeardown(function() {
          document.body.removeChild(el);
        });

        test('setup', function() {
          assert(!el.__boundListeners, 'listeners are not bound');
          el.setup();
          var boundListeners = el.__boundListeners;
          assert(boundListeners, 'listeners are bound');
          var outerMap = boundListeners.get(el);
          assert(outerMap, 'element events have handlers');
          var innerMap = boundListeners.get(el.$.inner);
          assert(innerMap, 'inner div events have handlers');
        });

        test('fire', function() {
          var options = {node: el.$.inner};
          el.fire('foo', {}, options);
          assert.equal(el._handled.div, 'foo', 'inner handler');
          assert.equal(el._handled[el.localName], 'foo', 'outer handler');
          el.fire('bar', {}, options);
          assert.equal(el._warned.length, 2);
          assert.equal(el._warned[0], 'listener method `missing` not defined');
          assert.equal(el._warned[1], 'listener method `missing` not defined');
        });

        test('teardown', function() {
          el.teardown();
          assert.deepEqual(el._removed[0], {target: el.localName, event: 'foo'});
          assert.deepEqual(el._removed[1], {target: 'div', event: 'foo'});
          assert.deepEqual(el._removed[2], {target: el.localName, event: 'bar'});
          assert.deepEqual(el._removed[3], {target: 'div', event: 'bar'});
        });
      });

      suite('Event Listener Cache', function() {
        suiteSetup(function() {
          el = document.createElement('x-double');
          document.body.appendChild(el);
          el.setup();
        });

        suiteTeardown(function() {
          document.body.removeChild(el);
        });

        test('listen marks event handler as listening', function() {
          var handler = el._recallEventHandler(el, 'foo', el, 'missing');
          assert.equal(handler._listening, true, 'handler should be marked');
        });

        test('Event handler fires only once', function() {
          el.fire('foo');
          assert.equal(el._warned.length, 1, 'event should fire only once');
        });

        test('listen reuses handler cache', function() {
          var expected = el._recallEventHandler(el, 'foo', el, 'missing');
          el.listen(el, 'foo', 'missing');
          var actual = el._recallEventHandler(el, 'foo', el, 'missing');
          assert.equal(actual, expected, 'function was not cached');
        });

        test('unlisten keeps handler for caching', function() {
          el.teardown();
          var fn = el._recallEventHandler(el, 'foo', el, 'missing');
          assert.ok(fn, 'should be cached');
        });

        test('unlisten markes cached handler as not listening', function() {
          var handler = el._recallEventHandler(el, 'foo', el, 'missing');
          assert.equal(handler._listening, false, 'handler should not be listening');
        });

        test('once unlistened, no handler fire', function() {
          el.fire('foo');
          assert.equal(el._warned.length, 1, 'event should not be handled anymore');
        });
      });
    });
  </script>

</body>
</html>
